package org.homonoia.worker.core;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.homonoia.model.core.NodeStatus;
import org.homonoia.model.query.QueryID;
import org.homonoia.model.task.Task;
import org.homonoia.worker.exceptions.StartupException;

public class WorkerNodeService implements Runnable {

    private static final Logger log = Logger.getLogger(WorkerNodeService.class);

    private Properties configProps = null;

    volatile private boolean stopped = true;
    volatile private NodeStatus status = null;

    public void loadConfiguration() throws StartupException {
	configProps = new Properties();
	try {
	    configProps.load(new FileReader("conf/configuration.properties"));
	} catch (FileNotFoundException e) {
	    log.error(e.getMessage(), e);
	    throw new StartupException(e.getMessage(), e.getCause());
	} catch (IOException e) {
	    log.error(e.getMessage(), e);
	    throw new StartupException(e.getMessage(), e.getCause());
	}

	try {
	    Class.forName(configProps.getProperty("DBDriver")).newInstance();
	} catch (ClassNotFoundException e) {
	    log.error(e.getMessage(), e);
	    throw new StartupException(e.getMessage(), e.getCause());
	} catch (InstantiationException e) {
	    log.error(e.getMessage(), e);
	    throw new StartupException(e.getMessage(), e.getCause());
	} catch (IllegalAccessException e) {
	    log.error(e.getMessage(), e);
	    throw new StartupException(e.getMessage(), e.getCause());
	}
    }

    public void run() {
	stopped = false;
	status = NodeStatus.AVAILABLE;

	long lTimeLastStatusUpdate = 0L;
	while (!stopped) {
	    final long lTimeCurrentUpdate = Calendar.getInstance().getTimeInMillis();
	    final long lTimeSinceLastStatusUpdate = lTimeCurrentUpdate - lTimeLastStatusUpdate;

	    if (lTimeSinceLastStatusUpdate > Long.parseLong(configProps
		    .getProperty("UpdateInterval"))) {
		sendStatusUpdate();

		lTimeLastStatusUpdate = lTimeCurrentUpdate;
	    } else {
		try {
		    Thread.sleep(1000);
		} catch (InterruptedException e) {
		    log.error(e.getMessage(), e);
		}
	    }
	}
    }

    public void stop() {
	stopped = true;
    }

    public void sendStatusUpdate() {
	PrintWriter out = null;
	Socket sock = null;

	try {
	    sock = new Socket(configProps.getProperty("Controller"), Integer.parseInt(configProps
		    .getProperty("ControllerPort")));

	    out = new PrintWriter(sock.getOutputStream(), true);
	    out.println("Request: application/update");
	    out.println("Status: " + status.toString().toUpperCase());
	} catch (NumberFormatException e) {
	    log.error(e.getMessage(), e);
	} catch (UnknownHostException e) {
	    log.error(e.getMessage(), e);
	} catch (IOException e) {
	    log.error(e.getMessage(), e);
	}
    }

    protected void setTaskCompleted() {
	status = NodeStatus.AVAILABLE;
    }

    public void addNewTask(QueryID id, Task task) {
	if (task != null && id != null && status == NodeStatus.AVAILABLE) {
	    status = NodeStatus.BUSY;
	    new Thread(new TaskRunner(this, task, id)).start();
	}
    }

    public final Properties getConfiguration() {
	return configProps;
    }

    public final boolean isStopped() {
	return stopped;
    }

    public final NodeStatus getStatus() {
	return status;
    }

    public boolean checkForTask() {
	return status == NodeStatus.AVAILABLE;
    }
}
